//=================================================================================================
// Particle system constant buffer.
//=================================================================================================
cbuffer cbPerFrame
{
	float3   gEyePosW;
	float3   gEmitPosW;
	float3   gEmitDirW;
	float    gGameTime;
	float    gTimeStep;
	float4x4 gViewProj; 
};


//=================================================================================================
// Net constant acceleration used to accerlate the particles.
//=================================================================================================
cbuffer cbFixed
{
	float3 gAccelW = { -1.0f, -9.8f, 0.0f };
};
 

//=================================================================================================
// Array of textures for texturing the particles.
//=================================================================================================
Texture2DArray gTexArray;


//=================================================================================================
// Random texture used to generate random numbers in shaders.
//=================================================================================================
Texture1D gRandomTex;


//=================================================================================================
// Linear sample state.
//=================================================================================================
SamplerState samLinear
{
	Filter   = MIN_MAG_MIP_LINEAR;
	AddressU = WRAP;
	AddressV = WRAP;
};


//=================================================================================================
// Disable depth stencil state.
//=================================================================================================
DepthStencilState DisableDepth
{
    DepthEnable    = FALSE;
    DepthWriteMask = ZERO;
};


//=================================================================================================
// Disable depth stencil write.
//=================================================================================================
DepthStencilState NoDepthWrites
{
    DepthEnable    = TRUE;
    DepthWriteMask = ZERO;
};


//=================================================================================================
// Generate random unit vectors.
//=================================================================================================
float3 RandUnitVec3(float offset)
{
	float  u = (gGameTime + offset);	
	float3 v = gRandomTex.SampleLevel(samLinear, u, 0).xyz;
	return normalize(v);
}


//=================================================================================================
// Generate random vectors.
//=================================================================================================
float3 RandVec3(float offset)
{
	float  u = (gGameTime + offset);
	float3 v = gRandomTex.SampleLevel(samLinear, u, 0).xyz;	
	return v;
}


//=================================================================================================
// SO Stage.
//=================================================================================================
#define PT_EMITTER 0
#define PT_FLARE   1
 

//=================================================================================================
// Particle struct.
//=================================================================================================
struct Particle
{
	float3 InitialPosW : POSITION;
	float3 InitialVelW : VELOCITY;
	float2 SizeW       : SIZE;
	float  Age         : AGE;
	uint   Type        : TYPE;
};


//=================================================================================================
// StreamOut VS.
//=================================================================================================
Particle StreamOutVS(Particle vin)
{
	return vin;
}


//=================================================================================================
// StreamOut GS.
//=================================================================================================
[maxvertexcount(6)]
void StreamOutGS(point Particle gin[1], inout PointStream<Particle> ptStream)
{	
	gin[0].Age += gTimeStep;
	if (gin[0].Type == PT_EMITTER && gin[0].Age > 0.002f)
	{
		for (int i = 0; i < 5; ++i)
		{
			float3 vRandom = 100.0f * RandVec3((float)i / 5.0f);
			vRandom.y = 70.0f;
			Particle particle;
			particle.InitialPosW = gEmitPosW.xyz + vRandom;
			particle.InitialVelW = float3(0.0f, 0.0f, 0.0f);
			particle.Age         = 0.0f;
			particle.SizeW       = float2(1.0f, 1.0f);
			particle.Type        = PT_FLARE;			
			ptStream.Append(particle);
		}
		gin[0].Age = 0.0f;
		ptStream.Append(gin[0]);
	}
	else if (gin[0].Type == PT_EMITTER && gin[0].Age <= 0.002f)
	{
		ptStream.Append(gin[0]);
	}
	else
	{
		if (gin[0].Age <= 5.0f) ptStream.Append(gin[0]);
	}		
}


//=================================================================================================
// StreamOut technique.
//=================================================================================================
GeometryShader gsStreamOut = ConstructGSWithSO(CompileShader(gs_5_0, StreamOutGS()), "POSITION.xyz; VELOCITY.xyz; SIZE.xy; AGE.x; TYPE.x");	
technique11 StreamOutTech
{
    pass P0
    {
        SetVertexShader(CompileShader(vs_5_0, StreamOutVS()));
        SetGeometryShader(gsStreamOut);
        SetPixelShader(NULL);
        SetDepthStencilState(DisableDepth, 0);
    }
}


//=================================================================================================
// Draw vertex out.
//=================================================================================================
struct VertexOut
{
	float3 PosW  : POSITION;
	uint   Type  : TYPE;
};


//=================================================================================================
// Draw VS.
//=================================================================================================
VertexOut DrawVS(Particle vin)
{
	VertexOut vout;	
	float t = vin.Age;
	vout.PosW = 0.5f * t * t * gAccelW + t * vin.InitialVelW + vin.InitialPosW;
	vout.Type  = vin.Type;	
	return vout;
}


//=================================================================================================
// Draw geo out.
//=================================================================================================
struct GeoOut
{
	float4 PosH  : SV_Position;
	float2 Tex   : TEXCOORD;
};


//=================================================================================================
// Draw GS.
//=================================================================================================
[maxvertexcount(2)]
void DrawGS(point VertexOut gin[1], inout LineStream<GeoOut> lineStream)
{	
	if( gin[0].Type != PT_EMITTER )
	{
		float3 p0 = gin[0].PosW;
		float3 p1 = gin[0].PosW + 0.05f * gAccelW;
		
		GeoOut v0;
		v0.PosH = mul(float4(p0, 1.0f), gViewProj);
		v0.Tex  = float2(0.0f, 0.0f);
		lineStream.Append(v0);
		
		GeoOut v1;
		v1.PosH = mul(float4(p1, 1.0f), gViewProj);
		v1.Tex  = float2(1.0f, 1.0f);
		lineStream.Append(v1);
	}
}


//=================================================================================================
// Draw PS.
//=================================================================================================
float4 DrawPS(GeoOut pin) : SV_TARGET
{
	return gTexArray.Sample(samLinear, float3(pin.Tex, 0));
}


//=================================================================================================
// Draw technique.
//=================================================================================================
technique11 DrawTech
{
    pass P0
    {
        SetVertexShader(CompileShader(vs_5_0, DrawVS()));
        SetGeometryShader(CompileShader(gs_5_0, DrawGS()));
        SetPixelShader(CompileShader(ps_5_0, DrawPS()));        
        SetDepthStencilState(NoDepthWrites, 0);
    }
}